package com.imooc.utils;

import com.imooc.exceptions.GraceException;
import com.imooc.grace.result.ResponseStatusEnum;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Encoder;
import javax.crypto.SecretKey;
import java.util.Date;
import static com.imooc.grace.result.ResponseStatusEnum.JWT_LENGTH_ERROR;

/**
 * @version 1.0
 * @Author Mr.Yang
 * @Date 2024-08-06 16:17
 * @question: jwt工具类
 */

@Component
@Slf4j
@RefreshScope //动态刷新数据
public class JWTUtils {

    public static final String at = "@";

    @Autowired
    private JWTProperties jwtProperties;

//    @Value("${auth.key}")
//    public String JWT_KEY;
    @Value("${jwt.key}")
    public String JWT_KEY;

    public String createJWTWithPrefix(String body, Long expireTimes, String prefix) {
        if (expireTimes == null)
            GraceException.display(ResponseStatusEnum.SYSTEM_NO_EXPIRE_ERROR);

        return prefix + at + createJWT(body, expireTimes);
    }

    public String createJWTWithPrefix(String body, String prefix) {
        return prefix + at + createJWT(body);
    }

    public String createJWT(String body) {
        return dealJWT(body, null);
    }

    public String createJWT(String body, Long expireTimes) {
        //如果传来的是null 需要抛出异常
        if (expireTimes == null)
            GraceException.display(ResponseStatusEnum.SYSTEM_NO_EXPIRE_ERROR);

        return dealJWT(body, expireTimes);
    }

    public String dealJWT(String body, Long expireTimes) {

//        String userKey = jwtProperties.getKey();
        String userKey = JWT_KEY;
        log.info("Nacos jwt key = " + JWT_KEY);

        //对秘钥进行base64编码
        String base64 = new BASE64Encoder().encode(userKey.getBytes());

        //对base64生成一个秘钥的对象
        SecretKey secretKey = Keys.hmacShaKeyFor(base64.getBytes());

        String jwt = "";
        if (expireTimes != null) {
            jwt = generatorJWT(body, expireTimes, secretKey);
        } else {
            jwt = generatorJWT(body, secretKey);
        }
        log.info("JWTUtils - dealJWT: generatorJWT = " + jwt);

        return jwt;
    }

    public String generatorJWT(String body, SecretKey secretKey) {
        String jwtToken = Jwts.builder()
                .setSubject(body)
                .signWith(secretKey)
                .compact();
        return jwtToken;
    }

    public String generatorJWT(String body, Long expireTimes, SecretKey secretKey) {
        //定义过期时间
        Date expireDate = new Date(System.currentTimeMillis() + expireTimes);
        String jwtToken = Jwts.builder()
                .setSubject(body)
                .signWith(secretKey)
                .setExpiration(expireDate)
                .compact();
        return jwtToken;
    }

    public String checkJWT(String pendingJWT) {

//        String userKey = jwtProperties.getKey();
        String userKey = JWT_KEY;
        log.info("Nacos jwt key = " + JWT_KEY);

        //对秘钥进行base64编码
        String base64 = new BASE64Encoder().encode(userKey.getBytes());

        //对base64生成一个秘钥的对象
        SecretKey secretKey = Keys.hmacShaKeyFor(base64.getBytes());

        //校验jwt
        JwtParser jwtParser = Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build();       //构造解析器
        //解析成功,可以获得Claims,从而去get相关的数据,如果此处抛出异常,则说明解析不通过,也就是token失效或者被篡改
        Jws<Claims> jws = jwtParser.parseClaimsJws(pendingJWT);      // 解析jwt

        /*//自加: 计算一下jwt现在还剩多长时间过期
        long currentTimeMillis = System.currentTimeMillis();
        Date expiration = jws.getBody().getExpiration();
        long timeToExpire = ( expiration.getTime() - currentTimeMillis ) / 1000;
        log.info("token距离过期还剩 " + timeToExpire + "秒");
        //自加: 如果时间少于30S 则自动刷新jwt
        String newJwt = this.refreshJWTIfNeeded(pendingJWT, 60 * 1000L, secretKey);
        log.info("新的jwt为:" + newJwt);

        Jws<Claims> newJws = jwtParser.parseClaimsJws(newJwt);

        String body = newJws.getBody().getSubject();*/

        String body = jws.getBody().getSubject();
        return body;
    }


    /**
     * jwt的刷新方法
     * @param oldJwt 此处的jwt是经过@符分割的 第二部分
     * @param newExpireTimeMillis 新的过期时间
     * @param secretKey
     * @return
     */
    public String refreshJWTIfNeeded(String oldJwt, Long newExpireTimeMillis, SecretKey secretKey) {
        try {
            //解析旧的JWT
            Claims claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(oldJwt)
                    .getBody();
            //body体中的 subject就是user信息

            //检查是否需要刷新(假设如果距离过期时间小于30秒,则刷新)
            long currentTimeMillis = System.currentTimeMillis();
            Date expiration = claims.getExpiration();
            long timeToExpire = expiration.getTime() - currentTimeMillis;
            if (timeToExpire <= 30000) { //30秒
                //生成新的JWT
                String newJwt = Jwts.builder()
                        .setSubject(claims.getSubject())
                        .signWith(secretKey)
//                        .setIssuedAt(new Date(currentTimeMillis))
                        .setExpiration(new Date(currentTimeMillis + newExpireTimeMillis))
                        .compact();
                return newJwt;
            }
            //如果不需要刷新，返回旧的JWT
            return oldJwt;
        } catch (Exception e) {
            //处理解析JWT时可能发生的异常 如JWT格式错误,签名不匹配等
            GraceException.display(JWT_LENGTH_ERROR);
            return null;
        }
    }


}

